#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;

//__thread_local_fix double A_slave[2][BLKX+2][BLKY+2][BLKZ+2];
__thread_local_fix double *A_slave_array;
//Record the logical information of this time executed task
__thread_local_fix int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
//Record the logical information of the last time executed task.
__thread_local_fix int last_iter,last_starti,last_startj,last_startk;

extern double A[2][SIZEX][SIZEY][SIZEZ];

#define alpha_d 0.0876
#define beta_d  0.0765
#define A_slave(d,i,j,k) A_slave_array[d*(s_endi-s_starti+2)*(s_endj-s_startj+2)*(s_endk-s_startk+2)+i*(s_endj-s_startj+2)*(s_endk-s_startk+2)+j*(s_endk-s_startk+2)+k]

void stencil_core(targs* args)                                      
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
   long  array_size;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = args->startk;
   s_endk = args->endk;
   array_size = 2*(s_endi-s_starti+2)*(s_endj-s_startj+2)*(s_endk-s_startk+2);
   A_slave_array = (double *)ldm_malloc(array_size * sizeof(double));
  /* printf("iter = %d, starti = %d,endi = %d,startj = %d,endj = %d,startk = %d,endk = %d\n",
             args->iter,args->starti,args->endi,args->startj,args->endj,args->startk,args->endk);*/
//if reuse fetch
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj && s_startk == last_startk)
   {
     int len, bsize, stride;
     get_reply = 0;
     stride = (SIZEZ-(s_endk-s_startk+2))*8;
     len = (s_endj-s_startj)*(s_endk-s_startk+2)*8;
     bsize = (s_endk-s_startk+2)*8;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk-1]
                 , &A_slave((last_iter%2),0,1,0)
                 , len, &get_reply
                 , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk-1]
                 , &A_slave((last_iter%2),(s_endi-s_starti+1),1,0)
                 , len, &get_reply
                 , 0, stride, bsize);
     for(i = s_starti; i < s_endi; ++i)
     {
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk]
                   , &A_slave((last_iter%2),(i-s_starti+1),0,1)
                   , (s_endk-s_startk)*8,&get_reply,0,0,0);
       athread_get(PE_MODE,&A[src][i][s_endj][s_startk]
                   , &A_slave((last_iter%2),(i-s_starti+1),(s_endj-s_startj+1),1)
                   , (s_endk-s_startk)*8,&get_reply,0,0,0);
     }
     for(i = s_starti; i < s_endi; ++i )
       for(j = s_startj; j < s_endj; ++j)
       {
          athread_get(PE_MODE,&A[src][i][j][s_startk-1]
                   , &A_slave((last_iter%2),(i-s_starti+1),(j-s_startj+1),0)
                   , 8,&get_reply,0,0,0);
          athread_get(PE_MODE,&A[src][i][j][s_endk]
                   , &A_slave((last_iter%2),(i-s_starti+1),(j-s_startj+1),(s_endk-s_startk+1))
                   , 8,&get_reply,0,0,0); 
      }
         while(get_reply != (2+(s_endi-s_starti)*2+(s_endi-s_starti)*(s_endj-s_startj)*2));
   }else{
//no reuse ldm
//1.1  fetch data
   get_reply=0;   
   for(i = s_starti-1; i < s_endi+1; i++)
   {   
       int tsize, bsize, stride;
       bsize=(s_endk-s_startk+2)*8;
       stride=(SIZEZ-(s_endk-s_startk+2))*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(s_endj-s_startj+2);
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk-1]
                 , &A_slave(src,(i-s_starti+1),0,0)
                 , tsize, &get_reply
                 , 0, stride, bsize);
   }
     while(get_reply!=(s_endi-s_starti+2));
  }

//2. computation
     for(i = 1; i < s_endi-s_starti+1; i++)
       for(j = 1; j < s_endj-s_startj+1; j++)
         for(k = 1; k < s_endk-s_startk+1; k++) 
            A_slave(dest,i,j,k) = alpha_d * (A_slave(src,i,j,k)) + \
                     beta_d * (A_slave(src,(i-1),j,k) + A_slave(src,(i+1),j,k) +\
                     A_slave(src,i,(j-1),k) + A_slave(src,i,(j+1),k)+\
                     A_slave(src,i,j,(k-1)) + A_slave(src,i,j,(k+1)));                      
//3.record information of last task
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
   last_startk = s_startk;
//4.1 write back
   put_reply=0;
   for(i =  s_starti; i < s_endi; ++i)
     for(j =  s_startj; j < s_endj; ++j)  
       athread_put(PE_MODE,&A_slave(dest,(i-s_starti+1),(j-s_startj+1),1)
                     , &A[dest][i][j][s_startk]
                     ,(s_endk-s_startk)*8,&put_reply,0,0);
//4.2 synchronize, wait for writing back finish.
   while(put_reply!=(s_endi-s_starti)*(s_endj-s_startj));
//5.free A_slave_array AND return to scheduler loop.
   ldm_free(A_slave_array,array_size * sizeof(double));
   
#ifdef __MAIN_MEM
//access main memory code
      int i,j,k;
      int  dest = (args->iter) % 2 ; 
      int  src = 1 - dest;
      for(i =  args->starti; i <  args->endi; i++)
        for(j =  args->startj; j <  args->endj; j++)
          for(k = args->startk; k <  args->endk; k++) 
          {   
             A[dest][i][j][k] = alpha_d * (A[(src)][(i)][(j)][(k)]) +\
                        beta_d * (A[src][i-1][j][k] + A[src][i+1][j][k] +\
                        A[src][i][j-1][k] + A[src][i][j+1][k] +\
                        A[src][i][j][k-1] + A[src][i][j][k+1]);
          }
#endif
}
